// ************************************************************* //
//                                                               //
//        Copyright (C) 2015 by Socionext Inc.                   //
//        All rights reserved.                                   //
//        Socionext Inc. Proprietary and Confidential.           //
//                                                               //
// ************************************************************* //
/*****************************************************************************/
/**
 * @file em_main.c
 * @brief eMMC main
 * @note None
 * @attention None
 *
 */
/*****************************************************************************/

// include -------------------------------------
// #include "common.h"
// #include "rom_main.h"
#include "em_int_cmn.h"
#include "em_config.h"
#include "em_if_ext.h"
#include "em_types.h"
#include "em_host.h"
#include "em_general.h"
#include "em_memorycard.h"
#include "em_data.h"

#undef FILEID
#define FILEID FILE_EM_MAIN

volatile unsigned char g_eMMC_HostRegister[0x400] __attribute__((section(".EXSEMMC")));
static int mwem_bread(void *devinfo, unsigned long start, unsigned long cnt, unsigned char* data);
static int mwem_bwrite(void *devinfo, unsigned long start, unsigned long cnt, unsigned char* data);

/*****************************************************************************/
/**
 * @brief MWEM_Init
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
int MWEM_Init(void)
{
	int ul_Ret;
	EM_CONFIG config = {0};

	//! register base address/descriptor area address
	config.ul_BaseAddress = EM_CONFIG_REGISTER_BASE_ADDRESS ;
	config.descLogAddress = (unsigned long*)sc_descriptor_area;

	//! initialize
	ul_Ret = em_init(
		(EM_HOST*)&sHosts,
		&config
	);
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_INIT, &g_sTracedata );
	}

	return ul_Ret;
}

/*****************************************************************************/
/**
 * @brief MWEM_Set_Config
 * @param[in] mode
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
int MWEM_Set_Config(unsigned int mode)
{
	int ul_Ret = EM_OK;

	/**
	 * Process start
	 */
	//! Access mode
	switch( mode ) {
		case 0 : 
			e_gAccessMode = EM_ACCESS_MODE_MMC_LEGACY;
			break;
		case 1 : 
			e_gAccessMode = EM_ACCESS_MODE_HS_SDR;
			break;
		case 2 : 
			e_gAccessMode = EM_ACCESS_MODE_HS_200;
			break;
		case 3 : 
			e_gAccessMode = EM_ACCESS_MODE_HS_400;
			break;
		default :
			break;
	}

	return ul_Ret;
}

/*****************************************************************************/
/**
 * @brief MWEM_Set_Config
 * @param[in] mode
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
int MWEM_Set_AccessPartition(unsigned int partition)
{
	int ul_Ret = EM_OK;

	/**
	 * parameter check
	 */
	if( partition > EM_ACCCESS_BOOT_RPMB ) {
		return EM_INVAL;
	}

	/**
	 * Process start
	 */
	//! AccessPartion
	if( e_gAccessPrttition != partition ){
		ul_Ret = em_setPartAccess(
			(EM_SLOT*)&(sHosts.sSlots[0]),
			(EM_PARTITION_ACCESS)partition
		);
	}
	if(ul_Ret == EM_OK) {
		e_gAccessPrttition = (EM_PARTITION_ACCESS)partition;
	}
	return ul_Ret;
}

/*****************************************************************************/
/**
 * @brief MWEM_Get_Config
 * @param[out] mode
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
int MWEM_Get_Config(unsigned int* mode)
{
	int ul_Ret = EM_OK;

	//! Access mode
	switch( e_gAccessMode ) {
		case EM_ACCESS_MODE_MMC_LEGACY : 
			*mode = 0;
			break;
		case EM_ACCESS_MODE_HS_SDR : 
			*mode = 1;
			break;
		case EM_ACCESS_MODE_HS_200 : 
			*mode = 2;
			break;
		case EM_ACCESS_MODE_HS_400 : 
			*mode = 3;
			break;
		default :
			ul_Ret = EM_INVAL;
			break;
	}
	return ul_Ret;
}

/*****************************************************************************/
/**
 * @brief MWEM_DeviceInit
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
int MWEM_DeviceInit(void)
{
	int ul_Ret;

	//! start
	ul_Ret = em_start(
		(EM_HOST*)&sHosts
	);
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, e_gAccessMode );
		DEBUG_TRACE( FACT_EM_INIT, &g_sTracedata );
	}

	return ul_Ret;
}

/*****************************************************************************/
/**
 * @brief mwem_bread
 * @param[in] devinfo
 * @param[in] start
 * @param[in] cnt
 * @param[in] data
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
static int mwem_bread(void *devinfo, unsigned long start, unsigned long cnt, unsigned char* data)
{
	unsigned long ul_Ret;
	unsigned long ul_DataSize;
	unsigned long tmp_cnt = 0;
	EM_DEVICE* EM_devinfo = devinfo;

	/**
	 * parameter check
	 */
	if( cnt == 0 ) {
		return EM_OK;
	}
	if ((start + cnt) > ((EM_devinfo->sCardInfo.ul_DeviceSizeMB * 1024 * 1024) / EM_CONFIG_DEFAULT_BLOCK_SIZE)) {
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	if( EM_devinfo->pSlot->uc_CardInserted != EM_ENABLE )
	{
		// not initialize
		DEBUG_TRACE_INFO_32( 0, EM_ERR_HARDWARE_PROBLEM );
		DEBUG_TRACE( FACT_EM_READ, &g_sTracedata );
		return EM_INVAL;
	}

	if(cnt > EM_CONFIG_MAX_SECTOR_COUNT) {
		// over 32MB
		tmp_cnt = (cnt - EM_CONFIG_MAX_SECTOR_COUNT);
		cnt     = EM_CONFIG_MAX_SECTOR_COUNT;
	}

	while (1) {
		ul_DataSize = (cnt * EM_CONFIG_DEFAULT_BLOCK_SIZE);

		//! read data
		ul_Ret = em_memoryCard_DataTransfer(
			EM_devinfo,
			start,
			(unsigned char*)data,
			ul_DataSize,
			EM_TRANSFER_READ
		);
		if( ul_Ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE_INFO_32( 4, e_gAccessMode );
			DEBUG_TRACE( FACT_EM_READ, &g_sTracedata );
			break;
		}

		if(tmp_cnt != 0) {
			data  += (EM_CONFIG_MAX_SECTOR_COUNT * EM_CONFIG_DEFAULT_BLOCK_SIZE);
			start += EM_CONFIG_MAX_SECTOR_COUNT;

			if(tmp_cnt >= EM_CONFIG_MAX_SECTOR_COUNT) {
				tmp_cnt -= EM_CONFIG_MAX_SECTOR_COUNT;
			}
			else {
				cnt     = tmp_cnt;
				tmp_cnt = 0;
			}
		}
		else {
			break;
		}
	}

	return ul_Ret;
}


#if 0	// Not used in Flash writer
/*****************************************************************************/
/**
 * @brief MWEM_Bread
 * @param[in] devinfo
 * @param[in] start
 * @param[in] cnt
 * @param[in] data
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
int MWEM_Bread(void *devinfo, unsigned long start, unsigned long cnt, char* data)
{
	unsigned long ul_Ret;
	EM_DEVICE* EM_devinfo = devinfo;

	/**
	 * parameter check
	 */
	if( devinfo == NULL ) {
		return EM_INVAL;
	}

	/**
	 * Process start
	 */
	start += EM_devinfo->pbr_secno;

	ul_Ret = mwem_bread(devinfo, start, cnt, (unsigned char*)data);
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, e_gAccessMode );
		DEBUG_TRACE( FACT_EM_READ, &g_sTracedata );
	}

	return ul_Ret;
}
#endif

/*****************************************************************************/
/**
 * @brief MWEM_Bread
 * @param[in] devinfo
 * @param[in] start
 * @param[in] cnt
 * @param[in] data
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
int MWEM_DirectRead(unsigned long start, unsigned long cnt, char* data)
{
	unsigned long ul_Ret;
	void *devinfo = (void*)sHosts.sSlots[0].pDevice;

	/**
	 * Process start
	 */
	ul_Ret = mwem_bread(devinfo, start, cnt, (unsigned char*)data);
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, e_gAccessMode );
		DEBUG_TRACE( FACT_EM_READ, &g_sTracedata );
	}
	return ul_Ret;
}

/*****************************************************************************/
/**
 * @brief mwem_bread
 * @param[in] devinfo
 * @param[in] start
 * @param[in] cnt
 * @param[in] data
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
static int mwem_bwrite(void *devinfo, unsigned long start, unsigned long cnt, unsigned char* data)
{
	unsigned long ul_Ret;
	unsigned long ul_DataSize;
	unsigned long tmp_cnt = 0;
	EM_DEVICE* EM_devinfo = devinfo;

	/**
	 * parameter check
	 */
	if( cnt == 0 ) {
		return EM_OK;
	}
	if ((start + cnt) > ((sHosts.sSlots[0].pDevice->sCardInfo.ul_DeviceSizeMB * 1024 * 1024) / EM_CONFIG_DEFAULT_BLOCK_SIZE)) {
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	if( sHosts.sSlots[0].uc_CardInserted != EM_ENABLE )
	{
		// not initialize
		DEBUG_TRACE_INFO_32( 0, EM_ERR_HARDWARE_PROBLEM );
		DEBUG_TRACE( FACT_EM_READ, &g_sTracedata );
		return EM_INVAL;
	}

	if(cnt > EM_CONFIG_MAX_SECTOR_COUNT) {
		// over 32MB
		tmp_cnt = (cnt - EM_CONFIG_MAX_SECTOR_COUNT);
		cnt     = EM_CONFIG_MAX_SECTOR_COUNT;
	}

	while (1) {
		ul_DataSize = (cnt * EM_CONFIG_DEFAULT_BLOCK_SIZE);

		//! write data
		ul_Ret = em_memoryCard_DataTransfer(
			EM_devinfo,
			start,
			(unsigned char*)data,
			ul_DataSize,
			EM_TRANSFER_WRITE
		);
		if( ul_Ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE_INFO_32( 4, e_gAccessMode );
			DEBUG_TRACE( FACT_EM_READ, &g_sTracedata );
			break;
		}

		if(tmp_cnt != 0) {
			data  += (EM_CONFIG_MAX_SECTOR_COUNT * EM_CONFIG_DEFAULT_BLOCK_SIZE);
			start += EM_CONFIG_MAX_SECTOR_COUNT;

			if(tmp_cnt >= EM_CONFIG_MAX_SECTOR_COUNT) {
				tmp_cnt -= EM_CONFIG_MAX_SECTOR_COUNT;
			}
			else {
				cnt     = tmp_cnt;
				tmp_cnt = 0;
			}
		}
		else {
			break;
		}
	}

	return ul_Ret;
}

#if 0	// Not used in Flash writer
/*****************************************************************************/
/**
 * @brief MWEM_Bread
 * @param[in] devinfo
 * @param[in] start
 * @param[in] cnt
 * @param[in] data
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
int MWEM_Bwrite(void *devinfo, unsigned long start, unsigned long cnt, char* data)
{
	unsigned long ul_Ret;
	EM_DEVICE* EM_devinfo = devinfo;

	/**
	 * parameter check
	 */
	if( devinfo == NULL ) {
		return EM_INVAL;
	}

	/**
	 * Process start
	 */
	start += EM_devinfo->pbr_secno;

	ul_Ret = mwem_bwrite(devinfo, start, cnt, (unsigned char*)data);
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, e_gAccessMode );
		DEBUG_TRACE( FACT_EM_READ, &g_sTracedata );
	}

	return ul_Ret;
}
#endif

/*****************************************************************************/
/**
 * @brief MWEM_Bread
 * @param[in] devinfo
 * @param[in] start
 * @param[in] cnt
 * @param[in] data
 * @return EM_OK	Normal end
 * @return other	Error occurred
 */
/*****************************************************************************/
int MWEM_DirectWrite(unsigned long start, unsigned long cnt, char* data)
{
	unsigned long ul_Ret;
	void *devinfo = (void*)sHosts.sSlots[0].pDevice;

	/**
	 * Process start
	 */
	ul_Ret = mwem_bwrite(devinfo, start, cnt, (unsigned char*)data);
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, e_gAccessMode );
		DEBUG_TRACE( FACT_EM_READ, &g_sTracedata );
	}
	return ul_Ret;
}

/*****************************************************************************/
/**
 * @brief MWEM_End
 */
/*****************************************************************************/
int MWEM_End( void )
{
	em_end( (EM_HOST*)&sHosts );

	return EM_OK;
}
